using System;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Workflow.Runtime;
using System.Workflow.ComponentModel.Compiler;
using System.Threading;
using System.Linq;

namespace Devfarm
{
    public class BootStrap
    {
        public static void AddResolver()
        {
            // Wire up the AssemblyResolver
            Assembly bootstrapperAsm = Assembly.Load("Devfarm.Bootstrapper, Version=2.3.0.0, Culture=neutral, PublicKeyToken=ff141f03102b1935");
            if (bootstrapperAsm == null)
            {
                string bootstrapperPath = "Devfarm.Bootstrapper.exe";
                // peel it out of our resources and run it
                Assembly exeAsm = Assembly.GetExecutingAssembly();
                string[] resnames = exeAsm.GetManifestResourceNames();
                if (resnames != null)
                {
                    var resname = resnames.Where(x => x.ToUpperInvariant().Contains("Devfarm.Bootstrapper.exe".ToUpperInvariant()));
                    if (resname != null && resname.Count() == 1)
                    {
                        using (Stream bootStream = exeAsm.GetManifestResourceStream(resname.Single()))
                        {
                            if (bootStream != null)
                            {
                                using (Stream file = File.OpenWrite(bootstrapperPath))
                                {
                                    CopyStream(bootStream, file);
                                    file.Close();
                                }
                            }
                            bootStream.Close();
                        }

                        Process.Start(bootstrapperPath).WaitForExit();

                        bootstrapperAsm = Assembly.Load("Devfarm.Bootstrapper, Version=2.3.0.0, Culture=neutral, PublicKeyToken=ff141f03102b1935");

                        try
                        {
                            File.Delete(bootstrapperPath);
                        }
                        catch (Exception) { }
                    }
                }

                if (bootstrapperAsm == null)
                {
                    System.Windows.Forms.MessageBox.Show("PowerWF runtime environment not found. Please install the PowerWF PowerShell module.", "Error");
                    return;
                }
            }

            bootstrapperAsm.CreateInstance("Devfarm.Bootstrapper.Injector");

            // Write this into the cache right away
            Assembly.Load("Devfarm.Automation.Powershellv2");

            // License Codejock
/*            Assembly uiAssembly = Assembly.Load("Devfarm.Common.UI");
            if (uiAssembly != null)
            {
                var licensorTypes = uiAssembly.GetTypes().Where(x => x.IsClass && x.FullName.Contains("LicenseCodejock"));
                foreach (var licensorType in licensorTypes)
                    Activator.CreateInstance(licensorType, true);
            } */
        }

        /// <summary>
        /// Copies the contents of input to output. Doesn't close either stream.
        /// </summary>
        public static void CopyStream(Stream input, Stream output)
        {
            byte[] buffer = new byte[8 * 1024];
            int len;
            while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, len);
            }
        }

        public static Dictionary<string, object> RunWorkflow(Type workflowType, Dictionary<string, object> parameters)
        {
            Dictionary<string, object> retval = null;

            using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
            {
                AutoResetEvent waitHandle = new AutoResetEvent(false);
                workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
                {
                    retval = e.OutputParameters;
                    waitHandle.Set();
                };
                workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
                {
                    Console.WriteLine(e.Exception.Message);
                    waitHandle.Set();
                };

                bool onlyYouCanPreventWildFires = true;
            RunWorkflow:
                try
                {
                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(workflowType, parameters);

                    instance.Start();
                    waitHandle.WaitOne();
                }
                catch (WorkflowValidationFailedException exp)
                {
                    ValidationErrorCollection list = exp.Errors;
                    foreach (ValidationError err in list)
                    {
                        Console.WriteLine(err.ErrorText);
                    }
                }
                catch (Exception exc)
                {
                    // System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.FileNotFoundException: Could not
                    // load file or assembly 'Devfarm.Dynamic.Module.PowerWF.Twitter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The
                    // system cannot find the file specified.
                    // File name: 'Devfarm.Dynamic.Module.PowerWF.Twitter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
                    if (exc != null &&
                       exc.InnerException != null &&
                       exc.InnerException is FileNotFoundException &&
                       exc.InnerException.Message.Contains("Devfarm.Dynamic") &&
                       onlyYouCanPreventWildFires)
                    {
                        // smoke em if you got em, we're going to try to rebuild the missing plugins
                        // binary depends on ...

                        Console.WriteLine("Building missing plugins ...");

                        onlyYouCanPreventWildFires = false;


                        string path = workflowType.Assembly.Location;

                        System.Diagnostics.Process p = new System.Diagnostics.Process();
                        p.StartInfo.FileName = "PowerShell.exe";
                        p.StartInfo.Arguments = string.Format(
                            "-Command \"Import-Module Powerwf; Install-MissingPlugins -Path '{0}'\"",
                            path);
                        p.StartInfo.RedirectStandardOutput = true;
                        p.StartInfo.UseShellExecute = false;
                        p.Start();
                        p.WaitForExit();
                        string imported = p.StandardOutput.ReadToEnd();

                        bool tryagain = !string.IsNullOrEmpty(imported) &&
                            System.Text.RegularExpressions.Regex.IsMatch(imported, @"\S") &&
                            imported.Contains("Installing");

                        Console.WriteLine(imported);

                        if (tryagain)
                            goto RunWorkflow;
                    }
                    Console.WriteLine(exc.ToString());
                    if (exc.InnerException != null)
                        Console.WriteLine(exc.InnerException.ToString());
                }
            }

            return retval;
        }

        public static Dictionary<string, object> GetWorkflowDefaultParms(Type workflowType)
        {
            Assembly configAssembly = Assembly.Load("Devfarm.Configuration.DLL");

            if (configAssembly == null)
                throw new NullReferenceException("Failed to locate Devfarm.Configuration.DLL");

            // Walk through each type in the assembly
            foreach (Type type in configAssembly.GetTypes())
            {
                if (type.IsPublic == true)
                {
                    if (type.FullName.Contains("ParameterMembershipHelper"))
                    {
                        MethodInfo methodInfo = type.GetMethod("GetWorkflowDefaultParms", System.Reflection.BindingFlags.Static | BindingFlags.Public);
                        if (methodInfo == null)
                            throw new NullReferenceException("Failed to locate GetWorkflowDefaultParms");

                        // Binding the method info to generic arguments
                        Type[] genericArguments = new Type[] { workflowType };

                        object returnValue = methodInfo.Invoke(null, new object[] { workflowType });
                        return returnValue as Dictionary<string, object>;
                    }
                }
            }
            return new Dictionary<string, object>();
        }

    }
}